// TrackViewNodes.cpp : implementation file
//

#include "stdafx.h"
#include "TrackViewSequences.h"
#include "TrackViewDialog.h"
#include "TrackViewUndo.h"
#include "StringDlg.h"
#include "Objects/SequenceObject.h"

//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CTrackViewSequencesCtrl, CTreeCtrlReport)
	ON_NOTIFY_REFLECT(NM_RCLICK, OnNMRclick)
	ON_NOTIFY_REFLECT(XTP_NM_REPORT_VALUECHANGED, OnValueChanged)
	ON_WM_LBUTTONDOWN( )
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////////
CTrackViewSequencesCtrl::CTrackViewSequencesCtrl()
{
	CMFCUtils::LoadTrueColorImageList( m_imageList,IDB_TRACKVIEW_NODES,16,RGB(255,0,255) );
	SetImageList( &m_imageList );

	CXTPReportColumn *pNodeCol   
		= AddColumn(new CXTPReportColumn(0, _T("Node"), 150, TRUE, XTP_REPORT_NOICON, TRUE, TRUE));
	pNodeCol->SetTreeColumn(true);
	pNodeCol->SetSortable(FALSE);

	ShowHeader(FALSE);

	SetMultipleSelection(FALSE);

	SetGroupRowsBold(TRUE);
	//SetTreeIndent(30);

	GetReportHeader()->AllowColumnRemove(FALSE);
	GetReportHeader()->AllowColumnSort(FALSE);

	AllowEdit(TRUE);
	EditOnClick(FALSE);
	m_pTrackViewDialog = NULL;
	m_bSelectionChangedHere = false;
};

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::RecursiveFill(CXTPReportRecords *pRecords, IAnimSequenceGroup *pGroup)
{
	COLORREF grayForLock = RGB(180,180,180);
	COLORREF blueForCutScene = RGB(0,0,255);
	COLORREF yellowBackForEarlyMovieUpdate = RGB(255,255,0);

	for(uint32 i=0; i<pGroup->GetChildSequencesCount(); ++i)
	{
		IAnimSequence *pSequence = pGroup->GetChildSequence(i);
		CRecord *pNewRecord = new CRecord(pSequence);
		
		if(pSequence->GetFlags() & IAnimSequence::PLAY_ONRESET
		&& pNewRecord->GetItem(0))
			pNewRecord->GetItem(0)->SetBold();
		if(pSequence->GetFlags() & IAnimSequence::CUT_SCENE
		&& pNewRecord->GetItem(0))
			pNewRecord->GetItem(0)->SetTextColor(blueForCutScene);

		if(pSequence->GetFlags() & IAnimSequence::EARLY_MOVIE_UPDATE
			&& pNewRecord->GetItem(0))
			pNewRecord->GetItem(0)->SetBackgroundColor(yellowBackForEarlyMovieUpdate);

		if(IsLocked(pSequence))
			pNewRecord->GetItem(0)->SetTextColor(grayForLock);

		pRecords->Add(pNewRecord);
	}

	for(uint32 i=0; i<pGroup->GetChildGroupsCount(); ++i)
	{
		IAnimSequenceGroup *pSequenceGroup = pGroup->GetChildGroup(i);
		CRecord *pNewRecord = new CRecord(pSequenceGroup);

		if(IsLocked(pSequenceGroup))
			pNewRecord->GetItem(0)->SetTextColor(grayForLock);

		RecursiveFill(pNewRecord->GetChilds(), pSequenceGroup);

		pNewRecord->SetExpanded(TRUE);
		pRecords->Add(pNewRecord);
	}
}

//////////////////////////////////////////////////////////////////////////
bool CTrackViewSequencesCtrl::IsLocked(const IAnimSequence *pAnimSequence)
{
	if(!pAnimSequence)
		return false;

	CSequenceObject* pObj = static_cast<CSequenceObject*>(pAnimSequence->GetOwner());
	if(pObj)
	{
		return pObj->GetLayer()->IsFrozen();
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CTrackViewSequencesCtrl::IsLocked(const IAnimSequenceGroup *pGroup)
{
	for(uint32 i=0; i<pGroup->GetChildSequencesCount(); ++i)
	{
		IAnimSequence *pSequence = pGroup->GetChildSequence(i);
		if(IsLocked(pSequence))
			return true;
	}

	for(uint32 i=0; i<pGroup->GetChildGroupsCount(); ++i)
	{
		IAnimSequenceGroup *pSequenceGroup = pGroup->GetChildGroup(i);
		
		if(IsLocked(pSequenceGroup))
			return true;
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::SetPopupMenuLock(CMenu* menu)
{
	if(!menu)
		return;

	UINT count = menu->GetMenuItemCount();
	for(UINT i=0; i<count;++i)
	{
		CString menuString;
		menu->GetMenuString(i,menuString, MF_BYPOSITION);

		if(menuString!="New Sequence" && menuString!="New Group")
			menu->EnableMenuItem(i,MF_DISABLED | MF_GRAYED | MF_BYPOSITION );
	}
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::OnFillItems()
{
	// To display items in an alphabetical order
	gEnv->pMovieSystem->GetRootGroup()->Sort();

	RecursiveFill(GetRecords(), gEnv->pMovieSystem->GetRootGroup());
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::OnSelectionChanged()
{
	if (!m_pTrackViewDialog)
		return;

	int nCount = GetSelectedRows()->GetCount();
	if (nCount == 1)
	{
		CRecord *pRecord = static_cast<CRecord*>(GetSelectedRows()->GetAt(0)->GetRecord());
		if (pRecord && pRecord->IsGroup() == false)
		{
			IAnimSequence *pSequence = pRecord->GetSequence();
			assert(pSequence);
			if (pSequence)
			{
				m_bSelectionChangedHere = true;
				m_pTrackViewDialog->SetCurrentSequence( pSequence );
				m_bSelectionChangedHere = false;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::OnNMRclick(NMHDR *pNMHDR, LRESULT *pResult)
{
	CPoint point;

	if (!m_pTrackViewDialog)
		return;

	CRecord *pSelectedRecord = NULL;

	// Find node under mouse.
	GetCursorPos( &point );
	ScreenToClient( &point );
	// Select the item that is at the point myPoint.

	CXTPReportRow* pRow = HitTest(point);
	if (pRow)
	{
		pSelectedRecord = static_cast<CRecord*>(pRow->GetRecord());
	}

	// Create pop up menu.
	CMenu menu;
	CMenu menuAddTrack;
	
	CMenu menuExpand;
	CMenu menuCollapse;

	const int NEW_SEQUENCE = 10;
	const int NEW_GROUP = 11;
	const int DELETE_SEQUENCE = 12;
	const int DELETE_GROUP = 13;
	const int SEQUENCE_PROPERTIES = 14;
	const int TOGGLE_AUTOSTART = 15;
	const int TOGGLE_CUTSCENE = 16;
	const int TOGGLE_NO_PLAYER = 17;
	const int TOGGLE_EARLYMOVIEUPDATE = 18;
	menu.CreatePopupMenu();

	menu.AppendMenu( MF_STRING,NEW_SEQUENCE,"New Sequence" );
	menu.AppendMenu( MF_STRING,NEW_GROUP,"New Group" );
	if (pSelectedRecord && pSelectedRecord->GetSequence())
	{
		assert(pSelectedRecord->IsGroup() == false);
		IAnimSequence *pSequence = pSelectedRecord->GetSequence();
		bool bLightAnimationSetSelected 
			= pSequence->GetFlags() & IAnimSequence::LIGHT_ANIMATION_SET;

		menu.AppendMenu( MF_SEPARATOR );
		menu.AppendMenu( MF_STRING,DELETE_SEQUENCE,"Delete Sequence" );
		if (bLightAnimationSetSelected == false)
		{
			menu.AppendMenu( MF_SEPARATOR );
			menu.AppendMenu( MF_STRING,SEQUENCE_PROPERTIES,"Properties..." );
			int seqFlags = pSequence->GetFlags();
			int flag = MF_STRING;
			if (seqFlags & IAnimSequence::PLAY_ONRESET)
				flag |= MF_CHECKED;
			menu.AppendMenu( flag,TOGGLE_AUTOSTART,"AutoStart" );
			flag = MF_STRING;
			if (seqFlags & IAnimSequence::CUT_SCENE)
				flag |= MF_CHECKED;
			menu.AppendMenu( flag,TOGGLE_CUTSCENE,"CutScene" );
			flag = MF_STRING;
			if (seqFlags & IAnimSequence::NO_PLAYER)
				flag |= MF_CHECKED;
			menu.AppendMenu( flag,TOGGLE_NO_PLAYER,"NoPlayer" );
			flag = MF_STRING;
			if (seqFlags & IAnimSequence::EARLY_MOVIE_UPDATE)
				flag |= MF_CHECKED;
			menu.AppendMenu( flag,TOGGLE_EARLYMOVIEUPDATE,"EarlyMovieUpdate" );
		}

		if(IsLocked(pSelectedRecord->GetSequence()))
			SetPopupMenuLock(&menu);
	}
	else if (pSelectedRecord && pSelectedRecord->GetSequenceGroup())
	{
		assert(pSelectedRecord->IsGroup() == true);
		menu.AppendMenu( MF_SEPARATOR );
		if (pSelectedRecord->GetChilds() && pSelectedRecord->GetChilds()->GetCount() > 0)
			menu.AppendMenu( MF_STRING|MF_GRAYED,DELETE_GROUP,"Delete Group" );
		else
			menu.AppendMenu( MF_STRING,DELETE_GROUP,"Delete Group" );

		if(IsLocked(pSelectedRecord->GetSequenceGroup()))
			SetPopupMenuLock(&menu);
	}

	GetCursorPos( &point );
	// track menu
	int cmd = CXTPCommandBars::TrackPopupMenu(&menu, TPM_NONOTIFY|TPM_RETURNCMD|TPM_LEFTALIGN|TPM_RIGHTBUTTON, 
																						point.x,point.y, this, NULL);

	CRecord *pParentRecord = NULL;
	if (pSelectedRecord)
	{
		if (pSelectedRecord->GetSequence())
			pParentRecord = static_cast<CRecord*>(pSelectedRecord->GetParentRecord());
		else
		{
			assert(pSelectedRecord->IsGroup());
			assert(pSelectedRecord->GetSequenceGroup());
			pParentRecord = pSelectedRecord;
		}
	}

	if (cmd == NEW_SEQUENCE)
	{
		// Create new sequence.
		if (pParentRecord)
		{
			CStringDlg dlg( "New Sequence" );
			if (dlg.DoModal() == IDOK)
			{
				CUndo undo("Add Sequence");
				string seq = pParentRecord->GetSequenceGroup()->GetFullName();
				seq += "/";
				seq += dlg.GetString().GetBuffer();
				if (!dlg.GetString().IsEmpty() && !GetIEditor()->GetMovieSystem()->FindSequence(seq.c_str()))
				{
					IAnimSequence *sequence 
						= GetIEditor()->GetMovieSystem()->CreateSequence(dlg.GetString().GetBuffer(), 
																														pParentRecord->GetSequenceGroup());
					GetIEditor()->GetObjectManager()->NewObject("SequenceObject", 0, seq.c_str());
					m_pTrackViewDialog->SetCurrentSequence( sequence );
				}
			}
		}
		else
			m_pTrackViewDialog->OnAddSequence();
	}
	else if (cmd == NEW_GROUP)
	{
		CStringDlg dlg( _T("Enter Group Name") );
		if (dlg.DoModal() == IDOK)
		{
			CUndo undo( "Add SequenceGroup" );
			CString fullName = dlg.GetString();
			if (pParentRecord)
			{
				CUndo::Record(new CUndoAnimSequenceGroupAdd(pParentRecord->GetSequenceGroup()));
				gEnv->pMovieSystem->CreateSequenceGroup(dlg.GetString().GetBuffer(), 
																								pParentRecord->GetSequenceGroup());
			}
			else
			{
				CUndo::Record(new CUndoAnimSequenceGroupAdd(gEnv->pMovieSystem->GetRootGroup()));
				gEnv->pMovieSystem->CreateSequenceGroup(dlg.GetString().GetBuffer()); 
			}

			Reload();
		}
	}
	else if (cmd == DELETE_SEQUENCE)
	{
		if (AfxMessageBox( "Delete Sequence? (you can undo this later.)",MB_YESNO|MB_ICONQUESTION ) == IDYES)
		{
			assert(pSelectedRecord && pSelectedRecord->GetSequence());
			string seqName = pSelectedRecord->GetSequence()->GetFullName();
			m_pTrackViewDialog->DeleteSequence(seqName.c_str());
		}
	}
	else if (cmd == DELETE_GROUP)
	{
		assert(pSelectedRecord && pSelectedRecord->GetSequenceGroup());
		CUndo undo( "Delete SequenceGroup" );
		CUndo::Record(new CUndoAnimSequenceGroupDelete(pSelectedRecord->GetSequenceGroup()));
		gEnv->pMovieSystem->DeleteSequenceGroup(pSelectedRecord->GetSequenceGroup());
		Reload();
	}
	else if (cmd == SEQUENCE_PROPERTIES)
	{
		m_pTrackViewDialog->OnEditSequence();
	}
	else if (cmd == TOGGLE_AUTOSTART)
	{
		assert(pSelectedRecord && pSelectedRecord->GetSequence());
		ToggleAutoStart(pSelectedRecord->GetSequence());

		int iFocused = GetFocusedRow()->GetIndex();
		Reload();
		SetFocusedRow(GetRows()->GetAt(iFocused));
	}
	else if (cmd == TOGGLE_CUTSCENE)
	{
		assert(pSelectedRecord && pSelectedRecord->GetSequence());
		ToggleSequenceFlag(pSelectedRecord->GetSequence(), IAnimSequence::CUT_SCENE);
		
		int iFocused = GetFocusedRow()->GetIndex();
		Reload();
		SetFocusedRow(GetRows()->GetAt(iFocused));
	}
	else if (cmd == TOGGLE_NO_PLAYER)
	{
		assert(pSelectedRecord && pSelectedRecord->GetSequence());
		ToggleSequenceFlag(pSelectedRecord->GetSequence(), IAnimSequence::NO_PLAYER);

		int iFocused = GetFocusedRow()->GetIndex();
		Reload();
		SetFocusedRow(GetRows()->GetAt(iFocused));
	}
	else if (cmd == TOGGLE_EARLYMOVIEUPDATE)
	{
		assert(pSelectedRecord && pSelectedRecord->GetSequence());
		ToggleSequenceFlag(pSelectedRecord->GetSequence(), IAnimSequence::EARLY_MOVIE_UPDATE);

		int iFocused = GetFocusedRow()->GetIndex();
		Reload();
		SetFocusedRow(GetRows()->GetAt(iFocused));
	}

	// processed
	*pResult = 1;
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::OnDragAndDrop( CXTPReportRow *pRow, CPoint absoluteCursorPos )
{
	CWnd *wnd = CWnd::WindowFromPoint( absoluteCursorPos );

	CPoint p = absoluteCursorPos;
	ScreenToClient(&p);

	CRect reportArea = GetReportRectangle();
	if (reportArea.PtInRect(p))
	{
		CXTPReportRow* pTargetRow = HitTest( p );
		CRecord *pRecordSrc = static_cast<CRecord*>(pRow->GetRecord());
		bool bMovingToGroup = pTargetRow == NULL || static_cast<CRecord*>(pTargetRow->GetRecord())->IsGroup();

		if (bMovingToGroup)
		{
			IAnimSequenceGroup *pGroup = gEnv->pMovieSystem->GetRootGroup();
			if (pTargetRow)
			{
				pGroup = static_cast<CRecord*>(pTargetRow->GetRecord())->GetSequenceGroup();
				assert(pGroup);
			}

			if (pRecordSrc->GetSequence())
			{
				IAnimSequence *pSequence = pRecordSrc->GetSequence();

				if(IsLocked(pSequence))
				{
					AfxMessageBox( "This sequence is locked with a frozen layer!",MB_OK|MB_ICONEXCLAMATION );
					return;
				}

				CUndo undo( "Reparent Sequence" );
				CUndo::Record( new CUndoAnimSequenceObject(pSequence) );

				pSequence->SetGroup(pGroup);
			}
			else
			{
				assert(pRecordSrc->GetSequenceGroup());

				if(IsLocked(pRecordSrc->GetSequenceGroup()))
				{
					AfxMessageBox( "Some sequence(s) in this group is(are) locked!",MB_OK|MB_ICONEXCLAMATION );
					return;
				}

				CUndo undo( "Reparent SequenceGroup" );
				CUndo::Record( new CUndoAnimSequenceGroupReparent(pRecordSrc->GetSequenceGroup()) );
				pRecordSrc->GetSequenceGroup()->SetParent(pGroup);
			}

			GetIEditor()->Notify( eNotify_OnUpdateTrackView );
		}
	}
}

//////////////////////////////////////////////////////////////////////////
// I had to override this in order to make top-level items drag-n-droppable.
void CTrackViewSequencesCtrl::OnLButtonDown( UINT nFlags, CPoint point )
{
	if (!m_bDragging) {
		__super::OnLButtonDown(nFlags,point);

		CRect reportArea = GetReportRectangle();
		if (reportArea.PtInRect(point))
		{
			CXTPReportRow* pRow = HitTest( point );
			if( pRow) 
			{
				m_ptDrag = point;
				m_bDragEx   = true;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::OnValueChanged(NMHDR*  pNotifyStruct, LRESULT* /*result*/)
{
	XTP_NM_REPORTRECORDITEM* pItemNotify = (XTP_NM_REPORTRECORDITEM*) pNotifyStruct;
	assert(pItemNotify != NULL);
	CRecord *pRecord = static_cast<CRecord*>(pItemNotify->pRow->GetRecord());
	CString newName = pItemNotify->pItem->GetCaption(pItemNotify->pColumn);

	if (newName.IsEmpty())
	{
		AfxMessageBox( "A sequence/group name cannot be empty!",MB_OK|MB_ICONEXCLAMATION );
		Reload();
		return;
	}
	else if (newName.Find('/') != -1)
	{
		AfxMessageBox( "A sequence/group name cannot contain a '/' character!",MB_OK|MB_ICONEXCLAMATION );
		Reload();
		return;
	}

	if (pRecord->GetSequence())
	{
		IAnimSequence *pSequence = pRecord->GetSequence();

		if(IsLocked(pSequence))
		{
			AfxMessageBox( "This sequence is locked with a frozen layer!",MB_OK|MB_ICONEXCLAMATION );
			Reload();
			return;
		}

		CUndo undo( "Rename Sequence" );
		CUndo::Record( new CUndoAnimSequenceObject(pSequence) );

		pSequence->SetName(newName.GetBuffer());
	}
	else
	{
		assert(pRecord->GetSequenceGroup());

		if(IsLocked(pRecord->GetSequenceGroup()))
		{
			AfxMessageBox( "Some sequence(s) in this group is(are) locked!",MB_OK|MB_ICONEXCLAMATION );
			Reload();
			return;
		}

		CUndo undo( "Rename SequenceGroup" );
		CUndo::Record( new CUndoAnimSequenceGroupRename(pRecord->GetSequenceGroup()) );
		pRecord->GetSequenceGroup()->SetName(newName.GetBuffer());
	}

	GetIEditor()->Notify( eNotify_OnUpdateTrackView );
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::SetCurrentSequence( IAnimSequence *pSeq )
{
	if(m_bSelectionChangedHere)
		return;

	for(int i = 0; i < GetRows()->GetCount(); ++i)
	{
		CRecord *pRecord = static_cast<CRecord*>(GetRows()->GetAt(i)->GetRecord());
		if(pRecord && pRecord->IsGroup() == false)
		{
			if(pRecord->GetSequence() == pSeq)
			{
				SetFocusedRow(GetRows()->GetAt(i));
				break;
			}
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::ToggleAutoStart(IAnimSequence *pSequence)
{
	CUndo undo( "AnimSequence(s) Modified" );
	CUndo::Record( new CUndoAnimSequenceObject(pSequence) );
	if (pSequence->GetFlags() & IAnimSequence::PLAY_ONRESET)
		pSequence->SetFlags(pSequence->GetFlags() & (~IAnimSequence::PLAY_ONRESET));
	else
	{
		// Clear all autostart sequences first.
		// More than one autostart sequences don't make much sense.
		IMovieSystem *pMovieSys = GetIEditor()->GetMovieSystem();
		for (int i=0; i<pMovieSys->GetNumSequences(); ++i)
		{
			IAnimSequence* pSeq = pMovieSys->GetSequence(i);
			if (pSeq->GetFlags() & IAnimSequence::PLAY_ONRESET)
			{
				CUndo::Record( new CUndoAnimSequenceObject(pSeq) );
				pSeq->SetFlags(pSeq->GetFlags() & (~IAnimSequence::PLAY_ONRESET));
			}
		}

		pSequence->SetFlags(pSequence->GetFlags() | IAnimSequence::PLAY_ONRESET);
	}
}

//////////////////////////////////////////////////////////////////////////
void CTrackViewSequencesCtrl::ToggleSequenceFlag(IAnimSequence *pSequence, int flag)
{
	CUndo undo( "AnimSequence Modified" );
	CUndo::Record( new CUndoAnimSequenceObject(pSequence) );
	if (pSequence->GetFlags() & flag)
	pSequence->SetFlags(pSequence->GetFlags() & (~flag));
	else
	pSequence->SetFlags(pSequence->GetFlags() | flag);
}